package com.ruoyi.system.service.impl.fxImpl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.config.WxV3PayConfig;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.enums.FxAppointmentEnum;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.wx.WXPayConstants;
import com.ruoyi.common.utils.wx.WXPaySignatureCertificateUtil;
import com.ruoyi.common.utils.wx.WXPayUtil;
import com.ruoyi.common.utils.wx.WxMiniPayUtils;
import com.ruoyi.system.domain.fx.FxAppointment;
import com.ruoyi.system.domain.fx.FxAppointmentItem;
import com.ruoyi.system.domain.fx.FxServeCategory;
import com.ruoyi.system.domain.fx.FxUser;
import com.ruoyi.system.domain.vo.FxAppointmentItemVo;
import com.ruoyi.system.domain.vo.FxAppointmentVo;
import com.ruoyi.system.mapper.fx.FxAppointmentMapper;
import com.ruoyi.system.mapper.fx.FxUserMapper;
import com.ruoyi.system.service.fx.IFxAppointmentItemService;
import com.ruoyi.system.service.fx.IFxAppointmentService;
import com.ruoyi.system.service.fx.IFxServeCategoryService;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 服务预约Service业务层处理
 *
 * @author ruoyi
 * @date 2025-04-29
 */
@Slf4j
@Service
public class FxAppointmentServiceImpl extends ServiceImpl<FxAppointmentMapper, FxAppointment> implements IFxAppointmentService
{
    @Autowired
    private FxAppointmentMapper fxAppointmentMapper;

    /**
     * 预约项
     */
    @Autowired
    private IFxAppointmentItemService fxAppointmentItemService;

    /**
     * 预约分类
     */
    @Autowired
    private IFxServeCategoryService fxServeCategoryService;

   /**
     * 预约分类
     */
    @Autowired
    private FxUserMapper fxUserMapper;

    /**
     * 查询服务预约
     *
     * @param id 服务预约主键
     * @return 服务预约
     */
    @Override
    public FxAppointmentVo selectFxAppointmentById(Long id)
    {
        FxAppointment fxAppointment = fxAppointmentMapper.selectFxAppointmentById(id);
        if (ObjectUtils.isEmpty(fxAppointment)){
            return null;
        }
        FxAppointmentVo fxAppointmentVo = BeanUtil.copyProperties(fxAppointment, FxAppointmentVo.class);
        List<FxAppointmentItem> list = fxAppointmentItemService.lambdaQuery().eq(FxAppointmentItem::getAppointmentId, fxAppointment.getId()).list();
        List<FxAppointmentItemVo> fxAppointmentItemVos = BeanUtil.copyToList(list, FxAppointmentItemVo.class);
        fxAppointmentVo.setFxAppointmentItemVos(fxAppointmentItemVos);
        return fxAppointmentVo;
    }

    /**
     * 查询服务预约列表
     *
     * @param fxAppointment 服务预约
     * @return 服务预约
     */
    @Override
    public List<FxAppointment> selectFxAppointmentList(FxAppointment fxAppointment)
    {
        SysUser user = SecurityUtils.getLoginUser().getUser();
        List<SysRole> roles = user.getRoles();

        boolean flag=false;
        for (SysRole role : roles) {
           if ( role.getRoleId().equals(2L)){
               flag=true;
           }
        }
        if (flag){
            fxAppointment.setUserId(user.getUserId());
        }
        return fxAppointmentMapper.selectFxAppointmentList(fxAppointment);
    }

    /**
     * 新增服务预约
     *
     * @param fxAppointment 服务预约
     * @return 结果
     */
    @Override
    @Transactional
    public String insertFxAppointment(FxAppointment fxAppointment)
    {
        if (ObjectUtils.isEmpty(fxAppointment)||ObjectUtils.isEmpty(fxAppointment.getContentId())){
            throw new RuntimeException("预约内容必填");
        }
        SysUser user = SecurityUtils.getLoginUser().getUser();
        Date nowDate = DateUtils.getNowDate();
        //预约服务内容
        FxServeCategory fxServeCategory = fxServeCategoryService.lambdaQuery().eq(FxServeCategory::getId, fxAppointment.getContentId()).one();
        //填充主订单属性
        fxAppointment.setCreateBy(user.getUserId().toString());
        fxAppointment.setUserId(user.getUserId());
        fxAppointment.setCreateTime(nowDate);
        fxAppointment.setOrderId(getOrderId());
        fxAppointment.setStatus(FxAppointmentEnum.AWAIT_PAY.getCode());
        //设置金额  定金 和总金额
        fxAppointment.setDeposit(fxServeCategory.getMoney());
        fxAppointment.setTotalAmount(fxServeCategory.getMoney());
        //新增主订单
        int i = fxAppointmentMapper.insertFxAppointment(fxAppointment);
        //默认将预约服务内容新增到子订单
        FxAppointmentItem fxAppointmentItem = new FxAppointmentItem();
        fxAppointmentItem.setAppointmentId(fxAppointment.getId());
        fxAppointmentItem.setServiceId(fxServeCategory.getId());
        fxAppointmentItem.setServiceName(fxServeCategory.getCategoryName());
        fxAppointmentItem.setServiceImg(fxServeCategory.getCategoryImage());
        fxAppointmentItem.setMoney(fxServeCategory.getMoney());
        fxAppointmentItem.setCreateBy(user.getUserId().toString());
        fxAppointmentItem.setCreateTime(nowDate);
        int i1 = fxAppointmentItemService.insertFxAppointmentItem(fxAppointmentItem);
        return i>0&&i1>0?fxAppointment.getOrderId():"";
    }

    /**
     * 修改服务预约
     *
     * @param fxAppointment 服务预约
     * @return 结果
     */
    @Override
    public int updateFxAppointment(FxAppointment fxAppointment)
    {
        fxAppointment.setUpdateTime(DateUtils.getNowDate());
        return fxAppointmentMapper.updateFxAppointment(fxAppointment);
    }

    /**
     * 批量删除服务预约
     *
     * @param ids 需要删除的服务预约主键
     * @return 结果
     */
    @Override
    public int deleteFxAppointmentByIds(Long[] ids)
    {
        return fxAppointmentMapper.deleteFxAppointmentByIds(ids);
    }

    /**
     * 删除服务预约信息
     *
     * @param id 服务预约主键
     * @return 结果
     */
    @Override
    public int deleteFxAppointmentById(Long id)
    {
        return fxAppointmentMapper.deleteFxAppointmentById(id);
    }


    /**
     * 预约单支付
     */
    @Override
    @Transactional
    public Map<String, Object> pay(String orderId) {
        FxAppointment fxAppointment = this.lambdaQuery().eq(FxAppointment::getOrderId, orderId).one();
        if (ObjectUtils.isEmpty(fxAppointment)){
            throw new RuntimeException("预约单不存在");
        }
        if (!fxAppointment.getStatus().equals(3L)){
            throw new RuntimeException("该订单状态无法支付");
        }
        Map<String, Object> map = new HashMap<>();

        BigDecimal totalMoney =  fxAppointment.getTotalAmount();

        Double moneyDouble = totalMoney.doubleValue() * 100;
        int money = (int) Math.floor(moneyDouble);
        try {
            String body = "喜房五金";
            //完成签名并执行请求
            FxUser fxUser = new FxUser();
            fxUser.setSysUserid(SecurityUtils.getUserId());

            String openid = fxUserMapper.selectFxUserList(fxUser).get(0).getOpenId();

            CloseableHttpResponse response = WxMiniPayUtils.creatOrderJSAPI(openid, orderId, money, body, WXPayConstants.APPOINTMENT_WECHAT_PAY_NOTIFY_URL);

            int statusCode = response.getStatusLine().getStatusCode();
            log.info("+++++++wx支付response++++++++++++" + response);
            if (statusCode == 200) { //处理成功
                String result = EntityUtils.toString(response.getEntity(), "UTF-8");
                JSONObject object = JSONObject.parseObject(result);
                //获取预付单
                String prepayId = object.getString("prepay_id");
                //生成签名
                Long timestamp = System.currentTimeMillis() / 1000;
                //随机字符串 这个是微信支付maven自带的 也可以用其它的
                String nonceStr = WXPayUtil.generateNonceStr();
                //生成带签名支付信息
                Map<String, String> param = new HashMap<>();
                param.put("appid", WxV3PayConfig.APP_ID);
                param.put("partnerid", WxV3PayConfig.Mch_ID);
                param.put("packageValue", prepayId);
                param.put("package", "prepay_id=" + prepayId);
                param.put("nonceStr", nonceStr);
                param.put("timestamp", String.valueOf(timestamp));
                param.put("signType", "MD5");
                // 加密串中包括 appId timeStamp nonceStr package signType 5个参数, 通过sdk WXPayUtil类加密, 注意, 此处使用 MD5加密 方式
                String signatureStr = Stream.of(WxV3PayConfig.APP_ID, String.valueOf(timestamp), nonceStr, "prepay_id=" + prepayId)
                        .collect(Collectors.joining("\n", "", "\n"));

                param.put("orderNum", orderId);
                param.put("paySign", WXPayUtil.getSign(signatureStr, WXPayConstants.PRIVATE_KEY_PATH));
                map.put("code", 200);
                map.put("message", "下单成功");
                map.put("data", param);
                return map;
            }
            map.put("code", 200);
            map.put("message", "下单失败");
            map.put("data", response);
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            map.put("code", 200);
            map.put("message", "下单失败");
            return map;
        }
    }

    /**
     * 预约单支付回调
     */
    @Override
    public Map<String, Object> payNotify(HttpServletRequest request, HttpServletResponse response) {
        log.info("+++++++预约单支付回调开始++++++++++++" + response);
        Map<String, Object> map = new HashMap<>();
        try {
            BufferedReader br = request.getReader();
            String str = null;
            StringBuilder sb = new StringBuilder();
            while ((str = br.readLine()) != null) {
                sb.append(str);
            }
            // 构建request，传入必要参数
            NotificationRequest requests = new NotificationRequest.Builder()
                    .withSerialNumber(request.getHeader("Wechatpay-Serial"))
                    .withNonce(request.getHeader("Wechatpay-Nonce"))
                    .withTimestamp(request.getHeader("Wechatpay-Timestamp"))
                    .withSignature(request.getHeader("Wechatpay-Signature"))
                    .withBody(String.valueOf(sb))
                    .build();
            //验签
            NotificationHandler handler = new NotificationHandler(WXPaySignatureCertificateUtil.getVerifier(WxV3PayConfig.mchSerialNo), WxV3PayConfig.apiV3Key.getBytes(StandardCharsets.UTF_8));
            //解析请求体
            Notification notification = handler.parse(requests);
            String decryptData = notification.getDecryptData();
            //解析
            JSONObject jsonObject = JSONObject.parseObject(decryptData);
            log.info(jsonObject.toString());
            //支付状态交易状态，枚举值： SUCCESS：支付成功 REFUND：转入退款 NOTPAY：未支付 CLOSED：已关闭 REVOKED：已撤销（付款码支付）
            // USERPAYING：用户支付中（付款码支付） PAYERROR：支付失败(其他原因，如银行返回失败)
            String trade_state = String.valueOf(jsonObject.get("trade_state"));
            if (trade_state.equals("SUCCESS")) {
                //订单号
                String orderNumber = String.valueOf(jsonObject.get("out_trade_no"));
                //微信支付微信生成的订单号
                String transactionId = String.valueOf(jsonObject.get("transaction_id"));
                String outRefundNo = String.valueOf(jsonObject.get("out_refund_no"));
                //修改预约单状态
                FxAppointment fxAppointment = this.lambdaQuery().eq(FxAppointment::getOrderId, orderNumber).one();
                fxAppointment.setTransactionId(transactionId);
                fxAppointment.setOutRefundNo(outRefundNo);
                fxAppointment.setPayTime(DateUtils.getNowDate());
                fxAppointment.setStatus(FxAppointmentEnum.AWAIT_SEND_ORDERS.getCode());
                this.updateById(fxAppointment);
                //此处处理业务
                map.put("code", "SUCCESS");
                map.put("message", "成功");
                //消息推送成功
                return map;
            }
            map.put("code", "RESOURCE_NOT_EXISTS");
            map.put("message", "订单不存在");
            return map;
        } catch (Exception e) {
            e.printStackTrace();
        }
        map.put("code", "FAIL");
        map.put("message", "失败");
        return map;
    }

    /**
     * 退单
     */
    @Override
    @Transactional
    public Map<String, Object> chargebackOrder(String orderId) {
        FxAppointment fxAppointment = this.lambdaQuery().eq(FxAppointment::getOrderId, orderId).one();
        if (ObjectUtils.isEmpty(fxAppointment)){
            throw new RuntimeException("订单不存在");
        }
        Map<String, Object> map = new HashMap<>();

        if (fxAppointment.getStatus().equals(FxAppointmentEnum.CHARGEBACK.getCode())){
            throw new RuntimeException("当前订单已退单");
        }
        String refund =   fxAppointment.getTotalAmount().multiply(new BigDecimal(100)).setScale(0, RoundingMode.DOWN).toString();
        try {
            //退款成功  修改状态
            fxAppointment.setStatus(FxAppointmentEnum.CHARGEBACK.getCode());
            this.updateById(fxAppointment);
            //完成签名并执行请求
            CloseableHttpResponse response = WxMiniPayUtils.chargebackOrderJSAPI(orderId, refund);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != 200) {
                throw new RuntimeException("退单申请失败");
            }
            map.put("code", 200);
            map.put("message", "退单已申请");
            return map;
        } catch (Exception e) {
            throw new RuntimeException("退单申请失败,请联系管理员");

        }
    }


    private String getOrderId(){
        String s = IdUtil.simpleUUID();
        String s1 = DateUtils.parseDateToStr("yyyyMMddHHmmssSSS", DateUtils.getNowDate());
        return "FX"+s1+s.substring(0,13);
    }
}
